// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件实现索引/切片表达式的类型检查。

package types

import (
	"go/ast"
	"go/constant"
	"go/internal/typeparams"
)

// 如果e是有效的函数实例化，indexExpr将返回true。
// 在这种情况下，x代表未实例化的函数值，而
// 调用方负责实例化函数。
func (check *Checker) indexExpr(x *operand, e *typeparams.IndexExpr) (isFuncInst bool) {
	check.exprOrType(x, e.X, true)
	// x可能是泛型

	switch x.mode {
	case invalid:
		check.use(e.Indices...)
		return false

	case typexpr:
		// 类型实例化
		x.mode = invalid
		// TODO（gri）在这里我们重新评估e.x-尝试避免这个
		x.typ = check.varType(e.Orig)
		if x.typ != Typ[Invalid] {
			x.mode = typexpr
		}
		return false

	case value:
		if sig, _ := under(x.typ).(*Signature); sig != nil && sig.TypeParams().Len() > 0 {
			// 函数实例化
			return true
		}
	}

	// x此时不应该是泛型的，但是请注意安全，检查
	check.nonGeneric(x)
	if x.mode == invalid {
		return false
	}

	// 普通索引表达式
	valid := false
	length := int64(-1) // 如果>=0，则有效
	switch typ := under(x.typ).(type) {
	case *Basic:
		if isString(typ) {
			valid = true
			if x.mode == constant_ {
				length = int64(len(constant.StringVal(x.val)))
			}
			// 索引字符串始终生成字节值
			// （不是常量），即使字符串和
			// 索引是常量
			x.mode = value
			x.typ = universeByte // 使用“字节”名称
		}

	case *Array:
		valid = true
		length = typ.len
		if x.mode != variable {
			x.mode = value
		}
		x.typ = typ.elem

	case *Pointer:
		if typ, _ := under(typ.base).(*Array); typ != nil {
			valid = true
			length = typ.len
			x.mode = variable
			x.typ = typ.elem
		}

	case *Slice:
		valid = true
		x.mode = variable
		x.typ = typ.elem

	case *Map:
		index := check.singleIndex(e)
		if index == nil {
			x.mode = invalid
			return false
		}
		var key operand
		check.expr(&key, index)
		check.assignment(&key, typ.key, "map index")
		// 即使索引失败，也可以继续-映射元素类型为已知
		x.mode = mapindex
		x.typ = typ.elem
		x.expr = e.Orig
		return false

	case *Interface:
		if !isTypeParam(x.typ) {
			break
		}
		// TODO（gri）报告详细的故障原因以获得更好的错误消息
		var key, elem Type // 密钥！=nil：我们必须拥有所有映射
		mode := variable   // 非映射结果模式
		// TODO（gri）因子输出闭包，并将其用于非类型参数情况以及
		if typ.typeSet().underIs(func(u Type) bool {
			l := int64(-1) // 如果>=0 
			var k, e Type  // k仅为映射
			switch t := u.(type) {
			case *Basic:
				if isString(t) {
					e = universeByte
					mode = value
				}
			case *Array:
				l = t.len
				e = t.elem
				if x.mode != variable {
					mode = value
				}
			case *Pointer:
				if t, _ := under(t.base).(*Array); t != nil {
					l = t.len
					e = t.elem
				}
			case *Slice:
				e = t.elem
			case *Map:
				k = t.key
				e = t.elem
			}
			if e == nil {
				return false
			}
			if elem == nil {
				// 第一类
				length = l
				key, elem = k, e
				return true
			}
			// 所有映射键必须相同（包括全部为零）
			if !Identical(key, k) {
				return false
			}
			// 所有元素类型必须相同
			if !Identical(elem, e) {
				return false
			}
			// 跟踪数组的最小长度，如果有
			if l >= 0 && l < length {
				length = l
			}
			return true
		}) {
			// 对于映射，索引表达式必须可分配给映射键类型。
			if key != nil {
				index := check.singleIndex(e)
				if index == nil {
					x.mode = invalid
					return false
				}
				var k operand
				check.expr(&k, index)
				check.assignment(&k, key, "map index")
				// 即使索引失败也可以继续-地图元素类型已知
				x.mode = mapindex
				x.typ = elem
				x.expr = e
				return false
			}

			// 没有地图
			valid = true
			x.mode = mode
			x.typ = elem
		}
	}

	if !valid {
		// types2在病理（无效）情况下（例如：类型T1[[[]T1{0][0]]T0）
		check.invalidOp(x, _NonIndexableOperand, "cannot index %s", x)
		x.mode = invalid
		return false
	}

	index := check.singleIndex(e)
	if index == nil {
		x.mode = invalid
		return false
	}

	// 可以在设置之前访问元素类型。确保我们有
	// 一个有效的类型。
	if x.typ == nil {
		x.typ = Typ[Invalid]
	}

	check.index(index, length)
	return false
}

func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) {
	check.expr(x, e.X)
	if x.mode == invalid {
		check.use(e.Low, e.High, e.Max)
		return
	}

	valid := false
	length := int64(-1) // 如果>=0，则有效
	switch u := coreString(x.typ).(type) {
	case nil:
		check.invalidOp(x, _NonSliceableOperand, "cannot slice %s: %s has no core type", x, x.typ)
		x.mode = invalid
		return

	case *Basic:
		if isString(u) {
			if e.Slice3 {
				at := e.Max
				if at == nil {
					at = e // e.Index[2]应该存在，但要小心
				}
				check.invalidOp(at, _InvalidSliceExpr, "3-index slice of string")
				x.mode = invalid
				return
			}
			valid = true
			if x.mode == constant_ {
				length = int64(len(constant.StringVal(x.val)))
			}
			// 规范：“对于非类型化字符串操作数，结果
			// 是字符串类型的非常量值。”
			if isUntyped(x.typ) {
				x.typ = Typ[String]
			}
		}

	case *Array:
		valid = true
		length = u.len
		if x.mode != variable {
			check.invalidOp(x, _NonSliceableOperand, "cannot slice %s (value not addressable)", x)
			x.mode = invalid
			return
		}
		x.typ = &Slice{elem: u.elem}

	case *Pointer:
		if u, _ := under(u.base).(*Array); u != nil {
			valid = true
			length = u.len
			x.typ = &Slice{elem: u.elem}
		}

	case *Slice:
		valid = true
		// x.typ不改变
	}

	if !valid {
		check.invalidOp(x, _NonSliceableOperand, "cannot slice %s", x)
		x.mode = invalid
		return
	}

	x.mode = value

	// spec:“只能省略第一个索引，默认为0。”
	if e.Slice3 && (e.High == nil || e.Max == nil) {
		check.invalidAST(inNode(e, e.Rbrack), "2nd and 3rd index required in 3-index slice")
		x.mode = invalid
		return
	}

	// 检查索引
	var ind [3]int64
	for i, expr := range []ast.Expr{e.Low, e.High, e.Max} {
		x := int64(-1)
		switch {
		case expr != nil:
			// 只有字符串、数组、
			// 和数组指针的“容量”静态已知，它与这些类型的
			// 的长度相同。
			max := int64(-1)
			if length >= 0 {
				max = length + 1
			}
			if _, v := check.index(expr, max); v >= 0 {
				x = v
			}
		case i == 0:
			// 第一个索引的默认值为0 
			x = 0
		case length >= 0:
			// 默认值为长度（=容量），否则
			x = length
		}
		ind[i] = x
	}

	// 常量索引必须在范围
	// （check.index已检查现有索引>=0）
L:
	for i, x := range ind[:len(ind)-1] {
		if x > 0 {
			for j, y := range ind[i+1:] {
				if y >= 0 && y < x {
					// 值y对应于表达式e.index[i+1+j]。
					// 因为y>=0，所以在检查索引时，它必须是从表达式
					// 设置的，因此e.Index[i+1+j]不是零。
					at := []ast.Expr{e.Low, e.High, e.Max}[i+1+j]
					check.errorf(at, _SwappedSliceIndices, "invalid slice indices: %d < %d", y, x)
					break L // 只报告一个错误，确定继续
				}
			}
		}
	}
}

// singleIndex返回索引表达式e中的（单个）索引。
// 如果缺少索引，或有多个索引，则报告错误
// 且结果为零。
func (check *Checker) singleIndex(expr *typeparams.IndexExpr) ast.Expr {
	if len(expr.Indices) == 0 {
		check.invalidAST(expr.Orig, "index expression %v with 0 indices", expr)
		return nil
	}
	if len(expr.Indices) > 1 {
		// TODO（rFindley）这应该得到一个明确的错误代码吗？
		check.invalidOp(expr.Indices[1], _InvalidIndex, "more than one index")
	}
	return expr.Indices[0]
}

// 索引检查索引表达式的有效性。
// 如果max>=0，则为索引的上限。
// 如果结果类型为！=Typ[Invalid]，索引是有效的，Typ是其（可能命名的）整数类型。
// 如果结果val>=0，则索引有效，val是其常量int值。
func (check *Checker) index(index ast.Expr, max int64) (typ Type, val int64) {
	typ = Typ[Invalid]
	val = -1

	var x operand
	check.expr(&x, index)
	if !check.isValidIndex(&x, _InvalidIndex, "index", false) {
		return
	}

	if x.mode != constant_ {
		return x.typ, -1
	}

	if x.val.Kind() == constant.Unknown {
		return
	}

	v, ok := constant.Int64Val(x.val)
	assert(ok)
	if max >= 0 && v >= max {
		check.invalidArg(&x, _InvalidIndex, "index %s is out of bounds", &x)
		return
	}

	// 0<=v[&&v<max]
	return x.typ, v
}

func (check *Checker) isValidIndex(x *operand, code errorCode, what string, allowNegative bool) bool {
	if x.mode == invalid {
		return false
	}

	// spec:“非类型化的常量索引被赋予int类型”
	check.convertUntyped(x, Typ[Int])
	if x.mode == invalid {
		return false
	}

	// spec:“索引x必须是整数类型或非类型化常量”
	if !allInteger(x.typ) {
		check.invalidArg(x, code, "%s %s must be integer", what, x)
		return false
	}

	if x.mode == constant_ {
		// spec
		if !allowNegative && constant.Sign(x.val) < 0 {
			check.invalidArg(x, code, "%s %s must not be negative", what, x)
			return false
		}

		// spec:“……并可由int类型的值表示”
		if !representableConst(x.val, check, Typ[Int], &x.val) {
			check.invalidArg(x, code, "%s %s overflows int", what, x)
			return false
		}
	}

	return true
}

// indexets检查数组或切片复合文字
// 的元素（ELT）与文字的元素类型（typ）之间的关系，以及根据
// 文字长度（如果已知）（长度>=0）进行的元素索引。它返回
// 文本的长度（最大索引值+1）。
// 
func (check *Checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64 {
	visited := make(map[int64]bool, len(elts))
	var index, max int64
	for _, e := range elts {
		// 确定并检查索引
		validIndex := false
		eval := e
		if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
			if typ, i := check.index(kv.Key, length); typ != Typ[Invalid] {
				if i >= 0 {
					index = i
					validIndex = true
				} else {
					check.errorf(e, _InvalidLitIndex, "index %s must be integer constant", kv.Key)
				}
			}
			eval = kv.Value
		} else if length >= 0 && index >= length {
			check.errorf(e, _OversizeArrayLit, "index %d is out of bounds (>= %d)", index, length)
		} else {
			validIndex = true
		}

		// 如果我们有一个有效的索引，请检查重复条目
		if validIndex {
			if visited[index] {
				check.errorf(e, _DuplicateLitKey, "duplicate index %d in array or slice literal", index)
			}
			visited[index] = true
		}
		index++
		if index > max {
			max = index
		}

		// 根据复合文字元素类型
		var x operand
		check.exprWithHint(&x, eval, typ)
		check.assignment(&x, typ, "array or slice literal")
	}
	return max
}
